package cn.mw.cmdb.link.strategy;

import cn.mw.cmdb.entity.InstanceInfo;
import cn.mw.cmdb.entity.InstanceInfoExtend;
import cn.mw.cmdb.enums.ZabbixAssetsStatusEnum;
import cn.mw.cmdb.link.entity.LinkHistoryTrendDto;
import cn.mw.cmdb.link.entity.MwLinkTargetIpInfo;
import cn.mw.cmdb.link.entity.MwNetWorkLinkDto;
import cn.mw.cmdb.link.enums.LinkDetectionWayEnum;
import cn.mw.cmdb.link.enums.LinkTrendValueTypeEnum;
import cn.mw.cmdb.link.param.LinkParam;
import cn.mw.cmdb.param.InstanceSearchParam;
import cn.mw.cmdb.param.zabbixResult.ItemApplication;
import cn.mw.cmdb.param.zabbixResult.MWItemHistoryDTO;
import cn.mw.cmdb.service.impl.CmdbServiceManage;
import cn.mw.cmdb.service.impl.InstanceServiceImpl;
import cn.mw.cmdb.util.BaseUtil;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.main.api.common.ZabbixDubboService;
import cn.mw.microMonitorCommon.constants.ZabbixApiConstants;
import cn.mw.microMonitorCommon.entity.UnitResult;
import cn.mw.microMonitorCommon.entity.zabbix.MWZabbixAPIResult;
import cn.mw.microMonitorCommon.util.DateUtils;
import cn.mw.microMonitorCommon.util.UnitUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description 线路检测方式处理
 */
@Slf4j
public abstract class LinkDetectionWayHandler {

    @DubboReference(check = false, timeout = 120000)
    public ZabbixDubboService zabbixDubboService;

    @Autowired
    public CmdbServiceManage cmdbServiceManage;

    public final String NORMAL = "正常";//正常
    public final String ABNORMAL = "异常";//异常

    public final String ROOT = "ROOT";
    public final String TARGET = "TARGET";
    public final String LOSS_RATE_UNIT = "%";
    public final String INTERFACE_IN_UTILIZATION = "INTERFACE_IN_UTILIZATION";
    public final String INTERFACE_OUT_UTILIZATION = "INTERFACE_OUT_UTILIZATION";
    public final String MW_INTERFACE_IN_TRAFFIC = "MW_INTERFACE_IN_TRAFFIC";
    public final String MW_INTERFACE_OUT_TRAFFIC = "MW_INTERFACE_OUT_TRAFFIC";

    public final String ICMP_RESPONSE_TIME = "ICMP_RESPONSE_TIME";

    public final String ICMP_LOSS = "ICMP_LOSS";
    public final String NQA_LOSS = "NQA_LOSS";

    public final String PING_AVGRTT = "PING_AVGRTT";
    public final String PING_MAXRTT = "PING_MAXRTT";
    public final String MS = "ms";

    public abstract LinkDetectionWayEnum handlerType();

    public abstract void getLinkMonitorInfo(List<MwNetWorkLinkDto> dtos) throws Exception;

    public abstract List<MwLinkTargetIpInfo> getTargetIpInfo(LinkParam linkParam) throws Exception;

    public abstract void handlerLinkData(MwNetWorkLinkDto netWorkLinkDto) throws Exception;

    public abstract  List<LinkHistoryTrendDto> getLinkHistoryTrend(MwNetWorkLinkDto netWorkLinkDto,LinkParam linkParam) throws Exception;

    public List<InstanceInfoExtend> getTargetDeviceAssetInfo(List<MwNetWorkLinkDto> dtos,String modelName,List<String> ids) throws Exception {
        //获取目标设备的资产信息
        InstanceSearchParam instanceSearchParam = new InstanceSearchParam();
        Map param = new HashMap();
        if(CollectionUtils.isNotEmpty(dtos)){
            List<String> ipAddress = dtos.stream().map(item -> item.getDetectionTargetIp()).collect(Collectors.toList());
            param.put(CMDBTool.getFieldName(InstanceInfo::getInBandIp), ipAddress);
        }
        if(StringUtils.isNotEmpty(modelName)){
            param.put(modelName, LinkDetectionWayEnum.ICMP.getType());
        }
        if(CollectionUtils.isNotEmpty(ids)){
            param.put(CMDBTool.getFieldName(InstanceInfo::getId), ids);
        }
        instanceSearchParam.setData(param);
        InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
        List<InstanceInfoExtend> infoExtends = instanceService.aggregateList(instanceSearchParam);
        if(CollectionUtils.isEmpty(infoExtends)){
            return infoExtends;
        }
        List<InstanceInfoExtend> instanceInfoExtends = infoExtends.stream().filter(item -> StringUtils.isNotEmpty(item.getItemAssetsStatus()) &&
                (item.getItemAssetsStatus().equals(ZabbixAssetsStatusEnum.NORMAL.getName()) || item.getItemAssetsStatus().equals(ZabbixAssetsStatusEnum.ALERT.getName())
                        || item.getItemAssetsStatus().equals(ZabbixAssetsStatusEnum.UNKNOWN.getName()))).collect(Collectors.toList());
        return instanceInfoExtends;
    }

    public Map<Integer,List<String>> getHostIdGroup(List<InstanceInfoExtend> infoExtends){
        Map<Integer, List<String>> serverGroup = infoExtends.stream().filter(item -> item.getMonitorServerId() != null && item.getMonitorServerId() != 0)
                .collect(Collectors.groupingBy(InstanceInfoExtend::getMonitorServerId, Collectors.mapping(InstanceInfoExtend::getMonitorTypeId, Collectors.toList())));
        return serverGroup;
    }

    public List<ItemApplication> getLinkZabbixMonitorInfo(Map<Integer, List<String>> serverGroup, List<String> itemNames){
        List<ItemApplication> itemApplications = new ArrayList<>();
        for (Map.Entry<Integer, List<String>> entry : serverGroup.entrySet()) {
            Integer serverId = entry.getKey();
            MWZabbixAPIResult mwZabbixAPIResult = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.ITEM_GET_BY_SEARCH, serverId, itemNames, entry.getValue());
            if(mwZabbixAPIResult == null || mwZabbixAPIResult.isFail()){continue;}
            List<ItemApplication> applicationList = JSONArray.parseArray(String.valueOf(mwZabbixAPIResult.getData()), ItemApplication.class);
            for (ItemApplication itemApplication : applicationList) {
                itemApplication.setServerId(serverId);
            }
            itemApplications.addAll(applicationList);
        }
        return itemApplications;
    }

    /**
     * 创建zabbix线路
     */
    public void createZabbixLink(LinkDetectionWayEnum wayEnum,String instanceId) throws Exception {
        //获取实例信息
        List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(null, null, Arrays.asList(instanceId));
        if (CollectionUtils.isEmpty(assetInfo)) {
            return;
        }
        InstanceInfoExtend instanceInfoExtend = assetInfo.get(0);
        String hostId = instanceInfoExtend.getMonitorTypeId();
        Integer monitorServerId = instanceInfoExtend.getMonitorServerId();
        //查询模板ID
        String templateId = templateGet(monitorServerId, wayEnum.getTemplateName());
        log.info("添加zabbix线路模板ID>>>"+templateId);
        if (StringUtils.isEmpty(templateId)) {
            log.info("添加zabbix线路没有获取到模板信息，模板名称::" + wayEnum.getTemplateName());
            return;
        }
        //先要判断这个资产有没有关联这个模板或者是否连接上这个子模板   1查询主机所有模板
        MWZabbixAPIResult result = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.HOST_GET_BY_TEMPALTE_ID, monitorServerId, hostId, templateId);
        log.info("添加zabbix线路查询主机模板>>>"+result);
        if (result.getCode() == 0) {
            com.alibaba.fastjson2.JSONArray data = com.alibaba.fastjson2.JSONArray.parseArray(JSON.toJSONString(result.getData()));
            log.info("添加zabbix线路查询主机模板data>>>"+data);
            //需要链接的模板已经存在于主机上
            if ((data.toString().endsWith("inherited from another template")) || (data.size() == 1 && data.get(0).toString().equals(hostId))) {
                return;
            }
            //添加线路
            MWZabbixAPIResult zabbixApiResult = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.HOST_MASS_UPDATE, monitorServerId, hostId, templateId);
            log.info("添加zabbix线路结果>>>"+zabbixApiResult);
        }
    }


    private String templateGet(Integer monitorServerId, String name) {
        MWZabbixAPIResult result = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.TEMPLATE_GET, monitorServerId, name, true);
        if (result.getCode() == 0) {
            com.alibaba.fastjson2.JSONArray jsonNode = com.alibaba.fastjson2.JSONArray.parseArray(JSON.toJSONString(result.getData()));
            String templateid = ((JSONObject) jsonNode.get(0)).get("templateid").toString();
            return templateid;
        }
        return null;
    }

    /**
     * 获取带宽利用率
     */
    public void getLinkBandWidthUtilization(List<MwNetWorkLinkDto> dtos) throws Exception {
        List<String> assetIds = new ArrayList<>();
        List<String> itemNames = new ArrayList<>();
        for (MwNetWorkLinkDto dto : dtos) {
            String takeValuePort = dto.getTakeValuePort();
            if(StringUtils.isEmpty(takeValuePort)){
                log.info("LinkDetectionWayHandler{} getLinkBandWidthUtilization() dto>>>"+dto);
                continue;
            }
            if(takeValuePort.equals(ROOT)){
                assetIds.add(dto.getSourceDeviceAsset());
                itemNames.add("["+dto.getSourceDevicePort()+"]"+MW_INTERFACE_IN_TRAFFIC);
                itemNames.add("["+dto.getSourceDevicePort()+"]"+MW_INTERFACE_OUT_TRAFFIC);
            }
            if(takeValuePort.equals(TARGET)){
                assetIds.add(dto.getTargetDeviceAsset());
                itemNames.add("["+dto.getTargetDevicePort()+"]"+MW_INTERFACE_IN_TRAFFIC);
                itemNames.add("["+dto.getTargetDevicePort()+"]"+MW_INTERFACE_OUT_TRAFFIC);
            }
        }
        List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(null, null, assetIds);
        log.info("assetInfo>>>>"+assetInfo);
        Map<String, String> assetMap = assetInfo.stream().collect(Collectors.toMap(s -> s.getId(), s -> s.getMonitorTypeId() + s.getMonitorServerId(),
                (existing, replacement) -> existing));
        Map<Integer, List<String>> hostIdGroup = getHostIdGroup(assetInfo);
        List<ItemApplication> itemApplications = getLinkZabbixMonitorInfo(hostIdGroup, itemNames);
        Map<String, List<ItemApplication>> itemMap = itemApplications.stream().collect(Collectors.groupingBy(item -> item.getHostid() + item.getServerId()));
        for (MwNetWorkLinkDto dto : dtos) {
            String assetId = null;
            String takeValuePort = dto.getTakeValuePort();
            if(StringUtils.isEmpty(takeValuePort)){
                continue;
            }
            String portName = null;
            if(takeValuePort.equals(ROOT)){
                assetId = dto.getSourceDeviceAsset();
                portName = dto.getSourceDevicePort();

            }
            if(takeValuePort.equals(TARGET)){
                assetId = dto.getTargetDeviceAsset();
                portName = dto.getTargetDevicePort();
            }
            String hostId = assetMap.get(assetId);
            List<ItemApplication> applications = itemMap.get(hostId);
            if(CollectionUtils.isEmpty(applications)){continue;}
            for (ItemApplication application : applications) {
                String name = application.getName();
                if(name.contains(MW_INTERFACE_IN_TRAFFIC) && name.contains("["+portName+"]")){
                    Double bandwidthUtilization = countLinkBandWidth(application, dto, dto.getUpBandWidth());
                    log.info("bandwidthUtilizationIN() bandwidthUtilization>>>"+bandwidthUtilization);
                    dto.setInLinkBandwidthUtilization(bandwidthUtilization);
                }
                if(name.contains(MW_INTERFACE_OUT_TRAFFIC) && name.contains("["+portName+"]")){
                    Double bandwidthUtilization = countLinkBandWidth(application, dto, dto.getDownBandWidth());
                    log.info("bandwidthUtilizationOUT() bandwidthUtilization>>>"+bandwidthUtilization);
                    dto.setOutLinkBandwidthUtilization(bandwidthUtilization);
                }
            }
        }
    }

    private Double countLinkBandWidth(ItemApplication application,MwNetWorkLinkDto dto,Double bandWidth){
        log.info("countLinkBandWidth() linkName>>>"+dto.getLinkName());
        log.info("countLinkBandWidth() bandWidth>>>"+bandWidth);
        if(bandWidth == 0){
            bandWidth = 1.0;
        }
        String bandWidthUnit = dto.getBandWidthUnit();
        String lastValue = application.getLastvalue();
        UnitResult unitResult = UnitUtil.getValueMap(lastValue, bandWidthUnit, application.getUnits());
        log.info("countLinkBandWidth() lastValue>>>"+lastValue);
        log.info("countLinkBandWidth() unitResult>>>"+unitResult);
        if(unitResult == null){
            return new BigDecimal((Double.parseDouble(lastValue) / bandWidth) * 100).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
        }
        return new BigDecimal((Double.parseDouble(unitResult.getValue()) / bandWidth) * 100).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public List<LinkHistoryTrendDto> getHistoryTrendData(MwNetWorkLinkDto netWorkLinkDto, LinkParam linkParam) throws Exception {
        List<LinkHistoryTrendDto> historyTrendDtos = new ArrayList<>();
        String takeValuePort = netWorkLinkDto.getTakeValuePort();
        String assetId = null;
        String port = null;
        if(takeValuePort.equals(ROOT)){
            assetId = netWorkLinkDto.getSourceDeviceAsset();
            port = netWorkLinkDto.getSourceDevicePort();
        }
        if(takeValuePort.equals(TARGET)){
            assetId = netWorkLinkDto.getTargetDeviceAsset();
            port = netWorkLinkDto.getTargetDevicePort();
        }
        List<String> itemNames = new ArrayList<>();
        List<Long> times = BaseUtil.getTimeIntervalByType(linkParam.getStartTime(), linkParam.getEndTime(), linkParam.getDateType());
        List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(null, null, Arrays.asList(assetId));
        if(CollectionUtils.isEmpty(assetInfo)){return historyTrendDtos;}
        InstanceInfoExtend instanceInfoExtend = assetInfo.get(0);
        itemNames.add("["+port+"]"+MW_INTERFACE_IN_TRAFFIC);
        itemNames.add("["+port+"]"+MW_INTERFACE_OUT_TRAFFIC);
        historyTrendDtos = handlerHistoryTrendData(instanceInfoExtend.getMonitorTypeId(), instanceInfoExtend.getMonitorServerId(), itemNames, times, linkParam.getHistoryDataType(), linkParam.getValueType());
        return historyTrendDtos;
    }

    public List<LinkHistoryTrendDto> handlerHistoryTrendData(String hostId,Integer serverId,List<String> itemNames,List<Long> times,Integer historyDataType,Integer valueType){
        log.info("LinkNQAWayHandler{} getLinkHistoryTrend() hostId>>>"+hostId+">>serverId>>>"+serverId+">>>itemNames>>>"+itemNames+">>>times>>>"+times+">>>historyDataType>>>"+historyDataType+">>>valueType>>>"+valueType);
        List<LinkHistoryTrendDto> historyTrendDtos = new ArrayList<>();
        MWZabbixAPIResult result = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.ITEM_GET_BY_SEARCH, serverId, itemNames, hostId);
        log.info("LinkNQAWayHandler{} getLinkHistoryTrend() result>>>"+result);
        if(result == null || result.isFail()){return historyTrendDtos;}
        List<ItemApplication> itemApplications = JSONArray.parseArray(String.valueOf(result.getData()), ItemApplication.class);
        if(CollectionUtils.isEmpty(itemApplications)){return historyTrendDtos;}
        Map<String, ItemApplication> itemMap = itemApplications.stream().collect(Collectors.toMap(s -> s.getItemid(), s -> s));
        List<String> itemIds = itemApplications.stream().map(ItemApplication::getItemid).collect(Collectors.toList());
        log.info("LinkNQAWayHandler{} getLinkHistoryTrend() itemIds>>>"+itemIds);
        MWZabbixAPIResult mwZabbixAPIResult = null;
        if(historyDataType == 1){
            mwZabbixAPIResult = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.HISTORY_GET_BY_TIME_AND_TYPE, serverId, itemIds, times.get(0), times.get(1), Integer.parseInt(itemApplications.get(0).getValue_type()));
        }
        if(historyDataType == 2){
            mwZabbixAPIResult = zabbixDubboService.getZabbixApiResult(ZabbixApiConstants.TREND_BATCH_GET, serverId, itemIds, times.get(0), times.get(1));
        }
        if(mwZabbixAPIResult == null || mwZabbixAPIResult.isFail()){return historyTrendDtos;}
        List<MWItemHistoryDTO> mwItemHistoryDTOS = JSONArray.parseArray(String.valueOf(mwZabbixAPIResult.getData()), MWItemHistoryDTO.class);
        log.info("LinkNQAWayHandler{} getLinkHistoryTrend() mwItemHistoryDTOS>>>"+mwItemHistoryDTOS);
        LinkTrendValueTypeEnum trendValueTypeEnum = LinkTrendValueTypeEnum.getTrendValueTypeEnum(valueType);
        log.info("LinkNQAWayHandler{} getLinkHistoryTrend() trendValueTypeEnum>>>"+trendValueTypeEnum);
        UnitResult unitResult = getUnitsConvert(trendValueTypeEnum,mwItemHistoryDTOS,itemApplications.get(0).getUnits());
        //如果查询的时响应时间，单位应该转为ms
        if(itemNames.contains(ICMP_RESPONSE_TIME) || itemNames.contains(PING_AVGRTT)){
            unitResult.setUnits(MS);
        }
        log.info("LinkNQAWayHandler{} getLinkHistoryTrend() unitResult>>>"+unitResult);
        //按时间分组
        Map<String, List<MWItemHistoryDTO>> listMap = mwItemHistoryDTOS.stream().collect(Collectors.groupingBy(item -> item.getClock()));
        for (Map.Entry<String, List<MWItemHistoryDTO>> entry : listMap.entrySet()) {
            LinkHistoryTrendDto dto = new LinkHistoryTrendDto();
            String clock = entry.getKey();
            List<MWItemHistoryDTO> value = entry.getValue();
            for (MWItemHistoryDTO mwItemHistoryDTO : value) {
                ItemApplication itemApplication = itemMap.get(mwItemHistoryDTO.getItemid());
                String name = itemApplication.getName();
                if(trendValueTypeEnum == null){
                    setMonitorValue(name,dto,mwItemHistoryDTO.getValue(),unitResult.getUnits(),itemApplications.get(0).getUnits());
                    continue;
                }
                switch (trendValueTypeEnum){
                    case AVG:
                        setMonitorValue(name,dto,mwItemHistoryDTO.getValue_avg(),unitResult.getUnits(),itemApplications.get(0).getUnits());
                        break;
                    case MAX:
                        setMonitorValue(name,dto,mwItemHistoryDTO.getValue_max(),unitResult.getUnits(),itemApplications.get(0).getUnits());
                        break;
                    case MIN:
                        setMonitorValue(name,dto,mwItemHistoryDTO.getValue_min(),unitResult.getUnits(),itemApplications.get(0).getUnits());
                        break;
                }
            }
            dto.setUnits(unitResult.getUnits());
            dto.setTime(DateUtils.formatDateTime(new Date(Long.parseLong(clock) * 1000)));
            dto.setClock(Long.parseLong(clock));
            historyTrendDtos.add(dto);
        }
        dataSort(historyTrendDtos);
        return historyTrendDtos;
    }

    private UnitResult getUnitsConvert(LinkTrendValueTypeEnum trendValueTypeEnum,List<MWItemHistoryDTO> mwItemHistoryDTOS,String oldUnits){
        UnitResult unitResult = null;
        if(trendValueTypeEnum == null){
            double avgValue = mwItemHistoryDTOS.stream().mapToDouble(item -> Double.parseDouble(item.getValue())).max().orElse(0);
            //进行合适的单位转换
            unitResult = UnitUtil.getConvertedValue(new BigDecimal(avgValue), oldUnits);
            return unitResult;
        }
        switch (trendValueTypeEnum){
            case AVG:
                double avgValue = mwItemHistoryDTOS.stream().mapToDouble(item -> Double.parseDouble(item.getValue_avg())).average().orElse(0);
                //进行合适的单位转换
                unitResult = UnitUtil.getConvertedValue(new BigDecimal(avgValue), oldUnits);
                return unitResult;
            case MAX:
                double maxValue = mwItemHistoryDTOS.stream().mapToDouble(item -> Double.parseDouble(item.getValue_max())).average().orElse(0);
                //进行合适的单位转换
                unitResult = UnitUtil.getConvertedValue(new BigDecimal(maxValue), oldUnits);
                return unitResult;
            case MIN:
                double minValue = mwItemHistoryDTOS.stream().mapToDouble(item -> Double.parseDouble(item.getValue_min())).average().orElse(0);
                //进行合适的单位转换
                unitResult = UnitUtil.getConvertedValue(new BigDecimal(minValue), oldUnits);
                return unitResult;
        }
        return unitResult;
    }

    private void dataSort(List<LinkHistoryTrendDto> historyTrendDtos){
        if(CollectionUtils.isEmpty(historyTrendDtos)){return;}
        Collections.sort(historyTrendDtos, new Comparator<LinkHistoryTrendDto>() {
            @Override
            public int compare(LinkHistoryTrendDto o1, LinkHistoryTrendDto o2) {
                if(o1.getClock() > o2.getClock()){
                    return 1;
                }
                if(o1.getClock() < o2.getClock()){
                    return -1;
                }
                return 0;
            }
        });
    }


    private void setMonitorValue(String name,LinkHistoryTrendDto trendDto,String value,String units,String oldUnits){
        UnitResult unitResult = UnitUtil.getValueMap(value, units, oldUnits);
        if(name.contains(MW_INTERFACE_IN_TRAFFIC)){
            trendDto.setInFlow(Double.parseDouble(unitResult.getValue()));
        }
        if(name.contains(MW_INTERFACE_OUT_TRAFFIC)){
            trendDto.setOutFlow(Double.parseDouble(unitResult.getValue()));
        }
        if(name.contains(ICMP_RESPONSE_TIME) || name.contains(PING_AVGRTT)){
            trendDto.setDelayed(Double.parseDouble(unitResult.getValue()));
        }
        if(name.contains(ICMP_LOSS) || name.contains(NQA_LOSS)){
            trendDto.setLossRate(Double.parseDouble(unitResult.getValue()));
        }
    }
}
